<template>
  <div class="app-wrapper" :style="backgroundStyle">
    <div class="chat-container">
      <header class="header">
        <div class="header-actions">
          <t-button variant="outline" @click="clearConfirm" class="sidebar-button">
            <template #icon>
              <DeleteIcon />
            </template>
            清空记录
          </t-button>
          <t-button variant="outline" @click="openShareModal" class="sidebar-button">
            <template #icon>
              <Share1Icon />
            </template>
            分享对话
          </t-button>
          <t-button variant="outline" @click="openDownloadModal" class="sidebar-button">
            下载对话
            <template #icon>
              <Download1Icon />
            </template>
          </t-button>
          <t-button variant="outline"  @click="toggleTheme">切换主题
            <template #icon>
              <BacktopIcon />
            </template>
          </t-button>
        </div>
      </header>
      <t-chat ref="chatRef" layout="single" style="height: 800px" :clear-history="chatList.length > 0 && !isStreamLoad"
        @clear="clearConfirm">
        <template v-for="(item, index) in chatList" :key="index">
          <t-chat-item :avatar="item.avatar" :name="item.name" :role="item.role" :datetime="item.datetime"
            :text-loading="index === 0 && loading">
            <template #content>
              <div :class="['chat-bubble', item.role === 'user' ? 'user-bubble' : 'assistant-bubble']"
                v-html="parseLatex(item.content)"></div>
            </template>
            <template #actions>
              <div class="custom-action-container">
                <t-button variant="outline" @click="openShareModal()" class="share-icon-button" title="分享"
                  aria-label="分享回答">
                  <template #icon>
                    <Share1Icon />
                  </template>
                </t-button>
                <t-button variant="outline" @click="confirmDelete(index)" class="delete-icon-button" title="删除"
                  aria-label="删除消息">
                  <template #icon>
                    <DeleteIcon />
                  </template>
                </t-button>
                <t-button variant="outline" @click="quoteMessage(index)" class="quote-icon-button" title="引用"
                  aria-label="引用消息">
                  <template #icon>
                    <ChatIcon />
                  </template>
                </t-button>
                <t-chat-action :is-good="isGood[index]" :is-bad="isBad[index]" :content="item.content"
                  @operation="(type, { e }) => handleOperation(type, { e, index })" class="custom-action-buttons">
                </t-chat-action>
              </div>
            </template>
          </t-chat-item>
        </template>
        <template #footer>
          <div class="chat-input">
            <t-chat-input :value="inputValue" @update:value="val => inputValue = val" :stop-disabled="isStreamLoad"
              @send="inputEnter" @stop="onStop"> </t-chat-input>
          </div>
        </template>
      </t-chat>
      <t-dialog
        v-model:visible="showDownloadModal"
        title="下载对话"
        :mask-closable="false"
        :show-close="true"
        width="400px"
      >
        <div>
          <p>请选择下载格式：</p>
          <div class="download-format-list">
            <t-button
              block
              class="download-btn-item"
              @click="downloadAndClose('word')"
            >
              <template #icon>
                <WordIcon />
              </template>
              Word
            </t-button>
            <br>
            <t-button
              block
              class="download-btn-item"
              @click="downloadAndClose('html')"
            >
              <template #icon>
                <HtmlIcon />
              </template>
              HTML
            </t-button>
            <br>
            <t-button
              block
              class="download-btn-item"
              @click="downloadAndClose('image')"
            >
              <template #icon>
                <ImageIcon />
              </template>
              长图
            </t-button>
          </div>
        </div>
        <template #footer>
          <t-button @click="closeDownloadModal">取消</t-button>
        </template>
      </t-dialog>


      <t-dialog v-model:visible="showShareModal" title="分享对话" :mask-closable="true" :show-close="true" width="400px">
        <div>
          <p>请选择分享平台：</p>
          <t-radio-group v-model="sharePlatform">
            <t-radio :value="'qq'">QQ</t-radio>
            <t-radio :value="'wechat'">微信</t-radio>
          </t-radio-group>
          <div style="margin-top: 16px;">
            <p>请输入分享说明：</p>
            <t-input v-model="shareText" placeholder="请输入文本..." />
          </div>
          <div style="margin-top: 16px;">
            <p>请输入图片链接以预览：</p>
            <t-input v-model="shareImageUrl" placeholder="粘贴图片链接..." />
            <div v-if="shareImageUrl" style="margin-top: 8px; text-align: center;">
              <img :src="shareImageUrl" alt="图片预览" style="max-width: 100%; max-height: 200px;" />
            </div>
          </div>
        </div>
        <template #footer>
          <t-button @click="closeShareModal">取消</t-button>
          <t-button type="primary" @click="handleShareSuccess">分享</t-button>
        </template>
      </t-dialog>

      <t-dialog v-model:visible="showDeleteConfirm" title="确认删除" :mask-closable="false" :show-close="false"
        width="400px">
        <div>
          <p>您确定删除该消息吗？</p>
        </div>
        <template #footer>
          <t-button @click="cancelDelete">取消</t-button>
          <t-button type="primary" @click="confirmDeleteMessage">确认删除</t-button>
        </template>
      </t-dialog>
    </div>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue';
import { MessagePlugin } from 'tdesign-vue-next';
import { saveAs } from 'file-saver';
import { DeleteIcon, ChatIcon, ViewListIcon, DownloadIcon, ShareIcon, Share1Icon, SearchIcon, BacktopIcon, Download1Icon } from 'tdesign-icons-vue-next';
import html2canvas from 'html2canvas';

const lightBackground = 'https://www.helloimg.com/i/2025/01/13/6784a3c2b8cf6.jpg';
const darkBackground = 'https://www.helloimg.com/i/2025/01/13/6784a3c37bfd3.jpg';

const downloadAndClose = (format) => {
  downloadFormat.value = format;
  downloadSelectedFormat();
  closeDownloadModal();
};

const backgroundStyle = computed(() => ({
  backgroundImage: `url(${changeTheme.value ? darkBackground : lightBackground})`,
  backgroundSize: 'cover',
  backgroundPosition: 'center',
  transition: 'background-image 0.5s ease-in-out',
}));

const showDeleteConfirm = ref(false);
const deleteIndex = ref(null);
const shareText = ref('');
const shareImageUrl = ref('');

const downloadAsWord = () => {
  const conversationHtml = chatList.value
    .map((msg) => {
      const time = new Date(msg.datetime).toLocaleString();
      const content = msg.content.replace(/<[^>]+>/g, '');
      return `<p><strong>[${time}] ${msg.name} (${msg.role}):</strong> ${content}</p>`;
    })
    .reverse()
    .join('');
  const htmlContent = `
    <html>
      <head>
        <meta charset="utf-8" />
        <title>对话记录</title>
      </head>
      <body>
        ${conversationHtml}
      </body>
    </html>
  `;
  const blob = new Blob([htmlContent], {
    type: 'application/msword;charset=utf-8'
  });
  saveAs(blob, 'conversation.doc');
};


const removeMarkdownSymbols = (text) => {
  return text
    .replace(/(\*\*|__)(.*?)\1/g, '$2')
    .replace(/(\*|_)(.*?)\1/g, '$2')
    .replace(/`{1,3}(.*?)`{1,3}/g, '$1')
    .replace(/!\[.*?\]\(.*?\)/g, '')
    .replace(/\[([^\]]+)\]\([^\)]+\)/g, '$1')
    .replace(/#+\s*(.*)/g, '$1')
    .replace(/>\s*(.*)/g, '$1')
    .replace(/-\s*(.*)/g, '$1')
    .replace(/\d+\.\s*(.*)/g, '$1')
    .replace(/\n{2,}/g, '\n');
};

const feedbackType = ref(null);
const currentFeedbackIndex = ref(null);
const showShareModal = ref(false);
const sharePlatform = ref('wechat');
const showDownloadModal = ref(false);
const downloadFormat = ref('markdown');
const chatRef = ref(null);
const changeTheme = ref(false);

const toggleTheme = () => {
  changeTheme.value = !changeTheme.value;
};

const openDownloadModal = () => {
  showDownloadModal.value = true;
};

const closeDownloadModal = () => {
  showDownloadModal.value = false;
};

const openShareModal = () => {
  showShareModal.value = true;
};

const closeShareModal = () => {
  showShareModal.value = false;
};

const handleShareSuccess = () => {
  closeShareModal();
  MessagePlugin.success('分享成功！');
};

const downloadSelectedFormat = () => {
  switch (downloadFormat.value) {
    case 'word': downloadAsWord(); break;
    case 'html': downloadAsHTML(); break;
    case 'image': downloadAsImage(); break;
  }
  closeDownloadModal();
};

const chatList = ref([
  {
    avatar: 'https://i.pravatar.cc/150?img=13',
    name: '程序员小勇',
    datetime: new Date().toLocaleString(),
    content: '你好！我是程序员小勇，很高兴为你提供帮助，你可以问我各种与编程有关的问题。',
    role: 'assistant',
  },
]);

const isGood = ref([]);
const isBad = ref([]);

const initializeFeedbackStates = () => {
  isGood.value = chatList.value.map(() => false);
  isBad.value = chatList.value.map(() => false);
};

initializeFeedbackStates();

const fetchCancel = ref(null);
const loading = ref(false);
const isStreamLoad = ref(false);

const clearConfirm = function () {
  chatList.value = [];
  initializeFeedbackStates();
};

const onStop = function () {
  if (fetchCancel.value) {
    fetchCancel.value.abort();
    loading.value = false;
    isStreamLoad.value = false;
  }
};

const handleOperation = function (type, options) {
  const { index } = options;
  if (type === 'good') {
    isGood.value[index] = !isGood.value[index];
    if (isGood.value[index]) {
      isBad.value[index] = false;
      feedbackType.value = 'good';
      currentFeedbackIndex.value = index;
    }
  } else if (type === 'bad') {
    isBad.value[index] = !isBad.value[index];
    if (isBad.value[index]) {
      isGood.value[index] = false;
      feedbackType.value = 'bad';
      currentFeedbackIndex.value = index;
    }
  } else if (type === 'replay') {
    const userQuery = chatList.value[index + 1].content;
    inputEnter(userQuery);
  }
};

const handleData = async (inputValue) => {
  loading.value = true;
  isStreamLoad.value = true;
  const lastItem = chatList.value[0];
  const messages = [
    { role: 'system', content: '你是一个热衷于编程的程序员，你很乐意向大家分享你的编程经历。', },
    { role: 'user', content: inputValue, }
  ];
  fetchSSE(messages, {
    success(result) {
      loading.value = false;
      const { data } = result;
      const cleanedContent = removeMarkdownSymbols(data?.delta?.content || '');
      lastItem.content += cleanedContent;
    },
    complete(isOk, msg) {
      if (!isOk || !lastItem.content) {
        lastItem.role = 'error';
        lastItem.content = msg;
      }
      isStreamLoad.value = false;
      loading.value = false;
    },
    cancel(cancel) {
      fetchCancel.value = cancel;
    },
  });
};

/**
 * Handles the input entry for the chat functionality.
 * 
 * This function is triggered when a user inputs a message. It first checks if the stream is loaded,
 * and if not, it proceeds to process the input. If the input value is empty, the function returns early.
 * 
 * The function creates a parameter object for the user's message, including avatar, name, datetime,
 * content, and role. It then adds this object to the beginning of the chat list.
 * 
 * Additionally, it creates a parameter object for the assistant's response, with an empty content field,
 * and adds it to the chat list. It also updates the isGood and isBad arrays to track user feedback.
 * 
 * Finally, it calls the handleData function with the modified input value.
 * 
 * @param {string} inputValue - The message entered by the user.
 */
const inputEnter = function (inputValue) {
  if (isStreamLoad.value) {
    return;
  }
  if (!inputValue) return;

  const modifiedInput = `${inputValue}`;

  const params = {
    avatar: 'https://i.pravatar.cc/150?img=37',
    name: '我',
    datetime: new Date().toLocaleString(),
    content: `${inputValue}`,
    role: 'user',
  };
  chatList.value.unshift(params);

  const params2 = {
    avatar: 'https://i.pravatar.cc/150?img=13',
    name: '程序员小勇',
    datetime: new Date().toLocaleString(),
    content: '',
    role: 'assistant',
  };
  chatList.value.unshift(params2);
  isGood.value.unshift(false);
  isBad.value.unshift(false);
  handleData(modifiedInput);
};

const fetchSSE = async (messages, options) => {
  const { success, fail, complete, cancel } = options;
  const controller = new AbortController();
  const { signal } = controller;
  cancel?.(controller);
  const apiKey = 'sk-6R0hq8U7v3bSbT1u41Lp6kPRwAgf9wnW73WgvSC7WUI73eRO';
  const responsePromise = fetch('/v1/chat/completions', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer${apiKey ? ` ${apiKey}` : ''}`,
    },
    body: JSON.stringify({
      messages,
      model: 'hunyuan-pro',
      stream: true,
    }),
    signal,
  }).catch((e) => {
    const msg = e.toString() || '流式接口异常';
    complete?.(false, msg);
    return Promise.reject(e);
  });

  responsePromise
    .then((response) => {
      if (!response?.ok) {
        complete?.(false, response.statusText);
        fail?.();
        throw new Error('Request failed');
      }
      const reader = response.body.getReader();
      const decoder = new TextDecoder();
      if (!reader) throw new Error('No reader available');

      const bufferArr = [];
      let dataText = '';
      const event = { type: null, data: null };

      async function processText({ done, value }) {
        if (done) {
          complete?.(true);
          return Promise.resolve();
        }
        const chunk = decoder.decode(value);
        const buffers = chunk.toString().split(/\r?\n/);
        bufferArr.push(...buffers);
        let i = 0;
        while (i < bufferArr.length) {
          const line = bufferArr[i];
          if (line) {
            dataText += line;
            const response = line.slice(6);
            if (response === '[DONE]') {
              event.type = 'finish';
              dataText = '';
            } else {
              const choices = JSON.parse(response.trim())?.choices?.[0];
              if (choices.finish_reason === 'stop') {
                event.type = 'finish';
                dataText = '';
              } else {
                event.type = 'delta';
                event.data = choices;
              }
            }
          }
          if (event.type && event.data) {
            const jsonData = JSON.parse(JSON.stringify(event));
            success(jsonData);
            event.type = null;
            event.data = null;
          }
          bufferArr.splice(i, 1);
        }
        return reader.read().then(processText);
      }

      return reader.read().then(processText);
    })
    .catch(() => {
      fail?.();
    });
};

const confirmDelete = (index) => {
  deleteIndex.value = index;
  showDeleteConfirm.value = true;
};

const confirmDeleteMessage = () => {
  if (deleteIndex.value !== null) {
    chatList.value.splice(deleteIndex.value, 1);
    isGood.value.splice(deleteIndex.value, 1);
    isBad.value.splice(deleteIndex.value, 1);
    MessagePlugin.success('消息已删除');
    deleteIndex.value = null;
    showDeleteConfirm.value = false;
  }
};

const cancelDelete = () => {
  deleteIndex.value = null;
  showDeleteConfirm.value = false;
};

const inputValue = ref('');

const quoteMessage = (index) => {
  const quotedContent = chatList.value[index].content;
  inputValue.value = `> ${quotedContent}`;
};
const downloadAsCSV = () => {
  let csvContent = '时间,发送者,角色,内容\n';
  chatList.value
    .slice()
    .reverse()
    .forEach(msg => {
      const time = new Date(msg.datetime).toLocaleString();
      const content = msg.content.replace(/<[^>]+>/g, '');
      const row = `"${time}","${msg.name}","${msg.role}","${content.replace(/"/g, '""')}"`;
      csvContent += row + '\n';
    });
  const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
  saveAs(blob, 'conversation.csv');
};

const downloadAsHTML = () => {
  const conversationHtml = chatList.value
    .map((msg) => {
      const time = new Date(msg.datetime).toLocaleString();
      const content = msg.content;
      return `<div style="margin-bottom: 1em;">
        <strong>[${time}] ${msg.name} (${msg.role})</strong><br>
        <div>${content}</div>
      </div>`;
    })
    .reverse()
    .join('');
  const htmlContent = `
    <html>
      <head>
        <meta charset="utf-8" />
        <title>对话记录</title>
      </head>
      <body>
        ${conversationHtml}
      </body>
    </html>
  `;
  const blob = new Blob([htmlContent], { type: 'text/html;charset=utf-8' });
  saveAs(blob, 'conversation.html');
};

const downloadAsImage = async () => {
  const element = document.querySelector('.t-chat');
  if (!element) {
    console.error('未找到.t-chat');
    return;
  }
  try {
    const canvas = await html2canvas(element, {
      scale: 2,
    });
    const dataURL = canvas.toDataURL('image/png');
    const link = document.createElement('a');
    link.href = dataURL;
    link.download = 'conversation.png';
    link.click();
  } catch (error) {
    console.error('长图截取失败:', error);
  }
};
</script>

<script>
import { ref } from 'vue';
import { renderToString } from 'katex';

const parseLatex = (text) => {
  if (!text) return '';
  let blockRegex = /\$\$([^$]+)\$\$/g;
  text = text.replace(blockRegex, (match, group1) => {
    try {
      return renderToString(group1, { displayMode: true });
    } catch (error) {
      console.error('KaTeX block parse error:', error);
      return match;
    }
  });
  let inlineRegex = /\$([^$]+)\$/g;
  text = text.replace(inlineRegex, (match, group1) => {
    try {
      return renderToString(group1, { displayMode: false });
    } catch (error) {
      console.error('KaTeX inline parse error:', error);
      return match;
    }
  });
  return text;
};
</script>
